﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Globalization;

namespace FlyingCircusWebIS.Models
{
    public class VooService : IVooService
    {
        IVooDAO vooDAO;
        IReservaDAO reservaDAO;
        IReservaService reservaService;
        IPessoaDAO pessoaDAO;

        public VooService()
        {
            vooDAO = new VooDAO();
            reservaDAO = new ReservaDAO();
            reservaService = new ReservaService();
            pessoaDAO = new PessoaDAO();
        }

        public void Adicionar(Voo entidade)
        {
            vooDAO.Adicionar(entidade);
        }

        public void Atualizar(Voo entidade)
        {
            vooDAO.Atualizar(entidade);
        }

        public void RemoverPorId(int id)
        {
            vooDAO.RemoverPorId(id);
        }

        public Voo BuscarPorId(int id)
        {
            Voo voo = vooDAO.BuscarPorId(id);
            return voo;
        }

        public List<Voo> Listar()
        {
            var voos = vooDAO.Listar();
            return voos;
        }

        public List<VooFormatado> ListarVooFormatado()
        {
            return vooDAO.ListarVooFormatado();
        }

        public List<ValidationResult> ValidarEntidade(Voo entidade)
        {
            List<ValidationResult> erros = new List<ValidationResult>();

            bool edicao = false;
            if (entidade.IdVoo != 0)
                edicao = true;

            if (edicao)
            {
                List<ValidationResult> errosAtualizar = ValidaAtualizar(entidade);
                if (errosAtualizar != null)
                    erros.AddRange(errosAtualizar);
            }
            else
            {
                List<ValidationResult> errosAdicionar = ValidaAdicionar(entidade);
                if (errosAdicionar != null)
                    erros.AddRange(errosAdicionar);
            }
            return erros;
        }

        public List<ValidationResult> ValidaAdicionar(Voo entidade)
        {

            List<ValidationResult> erros = new List<ValidationResult>();

            bool dataInvalida = entidade.DataPartida <= DateTime.Now || entidade.DataPartida >= entidade.DataChegada;
            if (dataInvalida)
                erros.Add(new ValidationResult("Não é possível cadastrar voos na data informada"));

            bool aeroportoInvalido = entidade.IdAeroportoChegada == entidade.IdAeroportoPartida;
            if (aeroportoInvalido)
                erros.Add(new ValidationResult("Os aeroportos de Partida e Chegada devem ser diferentes"));

            bool selecioneAeroporto = entidade.IdAeroportoPartida == 0 || entidade.IdAeroportoChegada == 0;
            if (selecioneAeroporto)
                erros.Add(new ValidationResult("Escolha os aeroportos de partida e de chegada"));

            //Se selecionou uma aeronave inválida, apresenta o erro
            bool aeronaveInvalida = entidade.IdAeronave <= 0;
            if (aeronaveInvalida)
            {
                erros.Add(new ValidationResult("Selecione uma aeronave disponível para realizar o Voo"));
            }
            else
            {
                //Caso contrário, verifica o numero de assentos
                AeronaveDAO aeronaveDAO = new AeronaveDAO();
                ModeloAeronaveDAO modeloAeronaveDAO = new ModeloAeronaveDAO();
                bool assentosInvalidos = entidade.AssentosComercializaveis > modeloAeronaveDAO.BuscarPorId(aeronaveDAO.BuscarPorId(entidade.IdAeronave).IdModeloAeronave).Assentos;
                if (assentosInvalidos)
                    erros.Add(new ValidationResult("A quantidade de assentos comercializados excede a capacidade do modelo"));
            }
            return erros;
        }


        public List<ValidationResult> ValidaAtualizar(Voo entidade)
        {
            List<ValidationResult> erros = new List<ValidationResult>();

            bool dataInvalida = entidade.DataPartida <= DateTime.Now || entidade.DataPartida >= entidade.DataChegada;
            if (dataInvalida)
                erros.Add(new ValidationResult("Não é possível cadastrar voos na data informada"));

            bool aeroportoInvalido = entidade.IdAeroportoChegada == entidade.IdAeroportoPartida;
            if (aeroportoInvalido)
                erros.Add(new ValidationResult("Os aeroportos de Partida e Chegada devem ser diferentes"));

            AeronaveDAO aeronaveDAO = new AeronaveDAO();
            ModeloAeronaveDAO modeloAeronaveDAO = new ModeloAeronaveDAO();
            bool assentosInvalidos = entidade.AssentosComercializaveis > modeloAeronaveDAO.BuscarPorId(aeronaveDAO.BuscarPorId(entidade.IdAeronave).IdModeloAeronave).Assentos;
            if (assentosInvalidos)
                erros.Add(new ValidationResult("A quantidade de assentos comercializados excede a capacidade do modelo"));

            //TODO fazer a va   lidação para alterar dados após uma reserva efetuada
            List<Reserva> reservas = reservaService.ListarReservasDoVoo(entidade.IdVoo);
            bool possuiReservas = reservas.Count > 0;
            if (possuiReservas)
                erros.Add(new ValidationResult("O registro não pode ser mais alterado. Existem dependências em outro contexto"));

            return erros;
        }

        public List<ValidationResult> ValidaRemoverPorId(int id)
        {
            List<ValidationResult> erros = new List<ValidationResult>();

            List<Reserva> reservas = reservaService.ListarReservasDoVoo(id);
            bool possuiReservas = reservas.Count > 0;
            if (possuiReservas)
                erros.Add(new ValidationResult("O registro não pode ser removido. Existem dependências em outro contexto"));


            return erros;
        }

        public List<Aeronave> ListarAeronavesDisponiveis(DateTime dataPartida, DateTime dataChegada)
        {
            return vooDAO.ListarAeronavesDisponiveis(dataPartida, dataChegada).FindAll(ae => ae.FlagAtivo.Equals("S"));
        }

        public List<SelectListItem> ListarItensAeronavesDisponiveis(DateTime dataPartida, DateTime dataChegada)
        {
            List<Aeronave> registro = ListarAeronavesDisponiveis(dataPartida, dataChegada);
            List<SelectListItem> itens = converterRegistrosAeronavesParaItens(registro);

            return itens;
        }


        private List<SelectListItem> converterRegistrosAeronavesParaItens(List<Aeronave> aeronaves)
        {
            List<SelectListItem> itens = new List<SelectListItem>();

            if (aeronaves.Count == 0)
            {
                itens.Add(new SelectListItem { Text = "<Não disponível>", Value = "" });
            }
            else
            {
                ModeloAeronaveDAO modeloAeronaveDAO = new ModeloAeronaveDAO();
                foreach (var aeronave in aeronaves)
                {
                    ModeloAeronave modeloAeronave = modeloAeronaveDAO.BuscarPorId(aeronave.IdModeloAeronave);
                    itens.Add(new SelectListItem { Text = modeloAeronave.Prefixo + " " + aeronave.Descricao + " - " + modeloAeronave.Assentos.ToString() + " assentos", Value = aeronave.IdAeronave.ToString(), Selected = true });

                }
            }

            return itens;
        }

        private bool IsDate(string data)
        {
            return IsDate(data, "dd/MM/yyyy HH:mm");
        }

        private bool IsDate(string data, string formato)
        {
            DateTime dataConvertida;
            bool dataValida;

            dataValida = DateTime.TryParseExact(data, formato, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out dataConvertida);

            return dataValida;
        }

        public List<ValidationResult> ValidarData(string data)
        {
            List<ValidationResult> erros = new List<ValidationResult>();
            if (!IsDate(data))
            {
                erros.Add(new ValidationResult("DataInvalida", "Horários de partida e chegada inválidos"));
            }
            return erros;
        }


        public List<Voo> Filtrar(string cidadePartida, string cidadeChegada, string dataInicial, string dataFinal)
        {

            var filtro = vooDAO.Listar().Where(v =>
                                            v.AeroportoPartida.Cidade.NomeCidade.StartsWith(cidadePartida, StringComparison.InvariantCultureIgnoreCase)
                                            &&
                                            v.AeroportoChegada.Cidade.NomeCidade.StartsWith(cidadeChegada, StringComparison.InvariantCultureIgnoreCase));

            if (string.IsNullOrEmpty(dataInicial))
            {
                // Caso a data não tenha sido especificada, então filtra os voos a partir do momento corrente + 1 dia
                filtro = filtro.Where(v => v.DataPartida >= DateTime.Now.AddDays(1));
            }
            else
            {
                // Filtra pela data inicial de partida especificada, caso ela seja maior ou igual que o momento corrente + 1 dia

                DateTime dataPartidaDT;
                try
                {
                    dataPartidaDT = DateTime.Parse(dataInicial);
                    if (DateTime.Compare(dataPartidaDT, DateTime.Now.AddDays(1)) < 0)
                        dataPartidaDT = DateTime.Now;

                }
                catch (Exception)
                {

                    dataPartidaDT = DateTime.Now.AddDays(1);
                }

                filtro = filtro.Where(v => v.DataPartida >= dataPartidaDT);
            }

            if (!string.IsNullOrEmpty(dataFinal))
            {
                // Filtra pela data final de partida especificada, caso ela seja maior ao momento corrente + 1 dia

                DateTime dataFinalDT;
                try
                {
                    dataFinalDT = DateTime.Parse(dataFinal);
                    if (DateTime.Compare(dataFinalDT, DateTime.Now.AddDays(1)) > 0)
                        filtro = filtro.Where(v => v.DataPartida <= dataFinalDT);

                }
                catch (Exception)
                {
                    // ?
                }

            }

            // Total de reservas tem que ser menor que o total de assentos
            filtro = filtro.Where(v => TotalReservasParaVoo(v.IdVoo) < v.AssentosComercializaveis);


            return filtro.ToList();

        }

        public int TotalReservasParaVoo(int idVoo)
        {
            return reservaDAO.CalcularQntReservasParaVoo(idVoo);
        }

        public List<Voo> ListarVoosFiltro(string localPartida, string localChegada, string idAeronave, string idPessoaUsuario)
        {
            List<Voo> encontrados = vooDAO.Listar().FindAll(v =>
                ((v.AeroportoPartida.NomeAeroporto.StartsWith(localPartida, StringComparison.CurrentCultureIgnoreCase) ||
                    v.AeroportoPartida.Cidade.NomeCidade.StartsWith(localPartida, StringComparison.CurrentCultureIgnoreCase)) &&

                (v.AeroportoChegada.NomeAeroporto.StartsWith(localChegada, StringComparison.CurrentCultureIgnoreCase) ||
                    v.AeroportoChegada.Cidade.NomeCidade.StartsWith(localChegada, StringComparison.CurrentCultureIgnoreCase)) &&

                (v.IdAeronave.ToString() == idAeronave || idAeronave == "") &&

                (v.IdPessoaUsuario.ToString() == idPessoaUsuario || idPessoaUsuario == ""))
                );

            return encontrados;
        }


        public List<Pessoa> ListarClientesPorIdVoo(int id)
        {
            List<Reserva> reservas = reservaService.ListarReservasDoVoo(id);
            List<Pessoa> pessoa = new List<Pessoa>();

            foreach (Reserva r in reservas)
            {
                pessoa.Add(pessoaDAO.BuscarPorId(r.IdPessoaCliente));
            }
            return pessoa;
        }


        public List<Voo> ListarUltimosPromocionais(int qnt = 6)
        {
            var promocionais = vooDAO.Listar().Where(e => e.VooPromocional.Equals("S") && e.DataPartida > DateTime.Now.AddDays(1));
            promocionais = promocionais.OrderByDescending(e => e.DataCadastro);

            return promocionais.Take(6).ToList();
        }
    }
}